home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / JFC.bin / JEditorPane.java < prev    next >
Text File  |  1998-06-30  |  18KB  |  555 lines

  1. /*
  2.  * @(#)JEditorPane.java    1.46 98/04/20
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20. package com.sun.java.swing;
  21.  
  22. import java.awt.*;
  23. import java.awt.event.*;
  24. import java.net.*;
  25. import java.util.Hashtable;
  26. import java.io.*;
  27.  
  28. import com.sun.java.swing.plaf.*;
  29. import com.sun.java.swing.text.*;
  30. import com.sun.java.swing.event.*;
  31. import com.sun.java.swing.text.html.*;
  32. import com.sun.java.accessibility.*;
  33.  
  34. /**
  35.  * A text pane to edit various kinds of content, such
  36.  * as html and rtf.  This component uses implementations of the 
  37.  * EditorKit to accomplish its behavior. It effectively
  38.  * morphs into the proper kind of text editor for the kind
  39.  * of content it is given.
  40.  * <p>
  41.  * The content type that editor is bound to at any given
  42.  * time is determined by the EditorKit currently installed.
  43.  * If the content is set to a new URL, its type is used
  44.  * to determine the EditorKit that should be used to load
  45.  * the content.
  46.  * <p>
  47.  * For the keyboard keys used by this component in the standard Look and
  48.  * Feel (L&F) renditions, see the
  49.  * <a href="doc-files/Key-Index.html#JEditorPane">JEditorPane</a> key assignments.
  50.  * <p>
  51.  * Warning: serialized objects of this class will not be compatible with
  52.  * future swing releases.  The current serialization support is appropriate 
  53.  * for short term storage or RMI between Swing1.0 applications.  It will
  54.  * not be possible to load serialized Swing1.0 objects with future releases
  55.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  56.  * baseline for the serialized form of Swing objects.
  57.  *
  58.  * @beaninfo
  59.  *   attribute: isContainer false
  60.  *
  61.  * @author  Timothy Prinzing
  62.  * @version 1.46 04/20/98
  63.  */
  64. public class JEditorPane extends JTextComponent {
  65.  
  66.     /**
  67.      * Constructs a new JEditorPane.  The document model is set to null.
  68.      */
  69.     public JEditorPane() {
  70.         super();
  71.     }
  72.  
  73.     /**
  74.      * Creates a JEditorPane based on a specified URL for input.
  75.      *
  76.      * @param initialPage the URL
  77.      * @exception IOException if the URL is null or cannot be accessed
  78.      */
  79.     public JEditorPane(URL initialPage) throws IOException {
  80.         this();
  81.         setPage(initialPage);
  82.     }
  83.  
  84.     /**
  85.      * Creates a JEditorPane based on a string containing a URL specification.
  86.      *
  87.      * @param url the URL
  88.      * @exception IOException if the URL is null or cannot be accessed
  89.      */
  90.     public JEditorPane(String url) throws IOException {
  91.         this();
  92.         setPage(url);
  93.     }
  94.  
  95.     /**
  96.      * Adds a hyperlink listener for notification of any changes, for example
  97.      * when a link is selected and entered.
  98.      *
  99.      * @param listener the listener
  100.      */
  101.     public synchronized void addHyperlinkListener(HyperlinkListener listener) {
  102.         listenerList.add(HyperlinkListener.class, listener);
  103.     }
  104.  
  105.     /**
  106.      * Removes a hyperlink listener.
  107.      *
  108.      * @param listener the listener
  109.      */
  110.     public synchronized void removeHyperlinkListener(HyperlinkListener listener) {
  111.         listenerList.remove(HyperlinkListener.class, listener);
  112.     }
  113.  
  114.     /**
  115.      * Notifies all listeners that have registered interest for
  116.      * notification on this event type.  This is normally called
  117.      * by the currently installed EditorKit if a content type
  118.      * that supports hyperlinks is currently active and there
  119.      * was activity with a link.  The listener list is processed
  120.      * last to first.
  121.      *
  122.      * @param e the event
  123.      * @see EventListenerList
  124.      */
  125.     public void fireHyperlinkUpdate(HyperlinkEvent e) {
  126.         // Guaranteed to return a non-null array
  127.         Object[] listeners = listenerList.getListenerList();
  128.         // Process the listeners last to first, notifying
  129.         // those that are interested in this event
  130.         for (int i = listeners.length-2; i>=0; i-=2) {
  131.             if (listeners[i]==HyperlinkListener.class) {
  132.                 ((HyperlinkListener)listeners[i+1]).hyperlinkUpdate(e);
  133.             }          
  134.         }
  135.     }
  136.  
  137.     /**
  138.      * Sets the current url being displayed.  The content type of the
  139.      * pane is set, and if the editor kit for the pane is non-null, then
  140.      * a new default document is created and the URL is read into it.
  141.      *
  142.      * @param page the URL of the page
  143.      * @exception IOException for a null or invalid page specification
  144.      */
  145.     public void setPage(URL page) throws IOException {
  146.         if (page == null) {
  147.             throw new IOException("invalid url");
  148.         }
  149.  
  150.         InputStream in = page.openStream();
  151.         URLConnection conn = page.openConnection();
  152.         String type = conn.getContentType();
  153.         setContentType(type);
  154.         if (kit != null) {
  155.             Document doc = kit.createDefaultDocument();
  156.             doc.putProperty(Document.StreamDescriptionProperty, page);
  157.             try {
  158.                 kit.read(in, doc, 0);
  159.                 setDocument(doc);
  160.             } catch (BadLocationException e) {
  161.                 throw new IOException(e.getMessage());
  162.             }
  163.         }
  164.     }
  165.  
  166.     /**
  167.      * Gets the current url being displayed.  If a URL was 
  168.      * not specified in the creation of the document, this
  169.      * will return null, and relative URL's will not be 
  170.      * resolved.
  171.      *
  172.      * @return the URL
  173.      */
  174.     public URL getPage() {
  175.         return (URL) getDocument().getProperty(Document.StreamDescriptionProperty);
  176.     }
  177.  
  178.     /**
  179.      * Sets the current url being displayed.
  180.      *
  181.      * @param url the URL for display
  182.      * @exception IOException for a null or invalid URL specification
  183.      */
  184.     public void setPage(String url) throws IOException {
  185.         if (url == null) {
  186.             throw new IOException("invalid url");
  187.         }
  188.         URL page = new URL(url);
  189.         setPage(page);
  190.     }
  191.  
  192.     /**
  193.      * Gets the class ID for the UI.
  194.      *
  195.      * @return the ID ("EditorPaneUI")
  196.      * @see JComponent#getUIClassID
  197.      * @see UIDefaults#getUI
  198.      */
  199.     public String getUIClassID() {
  200.         return "EditorPaneUI";
  201.     }
  202.  
  203.     /**
  204.      * Creates the default editor kit (PlainEditorKit) for when
  205.      * the component is first created.
  206.      *
  207.      * @return the editor kit
  208.      */
  209.     protected EditorKit createDefaultEditorKit() {
  210.         return new PlainEditorKit();
  211.     }
  212.  
  213.     /**
  214.      * Fetches the currently installed kit for handling
  215.      * content.  createDefaultEditorKit() is called to set up a default
  216.      * if necessary.
  217.      *
  218.      * @return the editor kit
  219.      */
  220.     public final EditorKit getEditorKit() {
  221.         if (kit == null) {
  222.             kit = createDefaultEditorKit();
  223.         }
  224.         return kit;
  225.     }
  226.  
  227.     /**
  228.      * Gets the type of content that this editor 
  229.      * handles.
  230.      *
  231.      * @return the content type, null if no editor kit set
  232.      */
  233.     public final String getContentType() {
  234.         return (kit != null) ? kit.getContentType() : null;
  235.     }
  236.  
  237.     /**
  238.      * Sets the type of content that this editor
  239.      * handles.  This calls <code>getEditorKitForContentType</code>,
  240.      * and then <code>setEditorKit</code> if an editor kit can
  241.      * be successfully located.  This is a convenience method
  242.      * that can be used as an alternative to calling 
  243.      * <code>setEditorKit</code> directly.
  244.      * 
  245.      * @param type the non-null mime type for the content editing
  246.      *   support.
  247.      * @see #getContentType
  248.      * @beaninfo
  249.      *  description: the type of content
  250.      */
  251.     public final void setContentType(String type) {
  252.         if ((kit == null) || (! type.equals(kit.getContentType()))) {
  253.             EditorKit k = getEditorKitForContentType(type);
  254.             if (k != null) {
  255.                 setEditorKit(k);
  256.             }
  257.         }
  258.     }
  259.  
  260.     /**
  261.      * Sets the currently installed kit for handling
  262.      * content.  This is the bound property that
  263.      * establishes the content type of the editor.
  264.      * Any old kit is first deinstalled, then if kit is non-null,
  265.      * the new kit is installed, and a default document created for it.
  266.      * A PropertyChange event ("editorKit") is always fired when
  267.      * setEditorKit() is called.
  268.      * 
  269.      * @param kit the desired editor behavior.
  270.      * @see #getEditorKit
  271.      * @beaninfo
  272.      *  description: the currently installed kit for handling content
  273.      *        bound: true
  274.      *       expert: true
  275.      */
  276.     public void setEditorKit(EditorKit kit) {
  277.         EditorKit old = this.kit;
  278.         if (old != null) {
  279.             old.deinstall(this);
  280.         }
  281.         this.kit = kit;
  282.         if (this.kit != null) {
  283.             this.kit.install(this);
  284.             setDocument(this.kit.createDefaultDocument());
  285.         }
  286.         firePropertyChange("editorKit", old, kit);
  287.     }
  288.  
  289.     /**
  290.      * Fetches the editor kit to use for the given type
  291.      * of content.  This is called when a type is requested
  292.      * that doesn't match the currently installed type.
  293.      * If the component doesn't have an EditorKit registered
  294.      * for the given type, it will try to create an 
  295.      * EditorKit from the default EditorKit registry.
  296.      * If that fails, a PlainEditorKit is used on the
  297.      * assumption that all text documents can be represented
  298.      * as plain text.
  299.      * <p>
  300.      * This method can be reimplemented to use some
  301.      * other kind of type registry.  This can
  302.      * be reimplemented to use the Java Activation
  303.      * Framework for example.
  304.      *
  305.      * @param type the non-null content type
  306.      * @return the editor kit
  307.      */  
  308.     public EditorKit getEditorKitForContentType(String type) {
  309.         if (typeHandlers == null) {
  310.             typeHandlers = new Hashtable(3);
  311.         }
  312.         EditorKit k = (EditorKit) typeHandlers.get(type);
  313.         if (k == null) {
  314.             k = createEditorKitForContentType(type);
  315.             if (k != null) {
  316.                 setEditorKitForContentType(type, k);
  317.             }
  318.         }
  319.         if (k == null) {
  320.             k = new DefaultEditorKit();
  321.         }
  322.         return k;
  323.     }
  324.  
  325.     /**
  326.      * Directly set the editor kit to use for the given type.  A 
  327.      * look-and-feel implementation might use this in conjunction
  328.      * with createEditorKitForContentType to install handlers for
  329.      * content types with a look-and-feel bias.
  330.      *
  331.      * @param type the non-null content type
  332.      * @param k the editor kit to be set
  333.      */
  334.     public void setEditorKitForContentType(String type, EditorKit k) {
  335.         if (typeHandlers == null) {
  336.             typeHandlers = new Hashtable(3);
  337.         }
  338.         typeHandlers.put(type, k);
  339.     }
  340.  
  341.     /**
  342.      * Create a handler for the given type from the default registry
  343.      * of editor kits.  The registry is created if necessary.  An attempt
  344.      * is made to dynamically load the prototype of the given kit.  If
  345.      * successful it is cloned and returned.
  346.      *
  347.      * @param type the content type
  348.      * @return the editor kit, or null if one cannot be created
  349.      */
  350.     public static EditorKit createEditorKitForContentType(String type) {
  351.         EditorKit k = null;
  352.         Hashtable kitRegistry = 
  353.             (Hashtable)SwingUtilities.appContextGet(kitRegistryKey);
  354.         if (kitRegistry == null) {
  355.             // nothing has been loaded yet.
  356.             kitRegistry = new Hashtable();
  357.             SwingUtilities.appContextPut(kitRegistryKey, kitRegistry);
  358.         } else {
  359.             k = (EditorKit) kitRegistry.get(type);
  360.         }
  361.         if (k == null) {
  362.             // try to dynamically load the support 
  363.             String classname = (String) getKitTypeRegistry().get(type);
  364.             try {
  365.                 Class c = Class.forName(classname);
  366.                 k = (EditorKit) c.newInstance();
  367.                 kitRegistry.put(type, k);
  368.             } catch (Throwable e) {
  369.                 e.printStackTrace();
  370.                 k = null;
  371.             }
  372.         }
  373.  
  374.         // create a copy of the prototype or null if there
  375.         // is no prototype.
  376.         if (k != null) {
  377.             return (EditorKit) k.clone();
  378.         }
  379.         return null;
  380.     }
  381.  
  382.     /**
  383.      * Establishes the default bindings of type to name.  
  384.      * The class will be dynamically loaded later when actually
  385.      * needed, and can be safely changed before attempted uses
  386.      * to avoid loading unwanted classes.
  387.      *
  388.      * @param type the non-null content type
  389.      * @param classname the class to load later
  390.      */
  391.     public static void registerEditorKitForContentType(String type, String classname) {
  392.         getKitTypeRegistry().put(type, classname);
  393.     }
  394.  
  395.     // --- JComponent methods ---------------------------------
  396.  
  397.     /**
  398.      * Turns off tab traversal once focus gained.
  399.      *
  400.      * @return true, to indicate that the focus is being managed
  401.      */
  402.     public boolean isManagingFocus() {
  403.         return true;
  404.     }
  405.  
  406.     // --- Scrollable  ----------------------------------------
  407.  
  408.     /**
  409.      * Returns true if a viewport should always force the width of this 
  410.      * Scrollable to match the width of the viewport.  
  411.      * 
  412.      * @return true if a viewport should force the Scrollables width to
  413.      * match its own.
  414.      */
  415.     public boolean getScrollableTracksViewportWidth() {
  416.         // PENDING(prinz) need to change this so the entire Scrollable
  417.         // interface comes from the installed EditorKit if it defines 
  418.         // Scrollable, because many content types have a kind of scrolling
  419.         // policy (eg html has preformatted areas that define minimum width,
  420.         // and rtf has a defined page size).
  421.         return true;
  422.     }
  423.  
  424.  
  425. /////////////////
  426. // Accessibility support
  427. ////////////////
  428.  
  429.  
  430.     /**
  431.      * Get the AccessibleContext associated with this JEditorPane.  A new
  432.      * context is created if necessary.
  433.      *
  434.      * @return the AccessibleContext of this JEditorPane
  435.      */
  436.     public AccessibleContext getAccessibleContext() {
  437.         if (accessibleContext == null) {
  438.             accessibleContext = new AccessibleJEditorPane();
  439.         }
  440.         return accessibleContext;
  441.     }
  442.  
  443.     /**
  444.      * The class used to obtain the accessible role for this object.
  445.      * <p>
  446.      * Warning: serialized objects of this class will not be compatible with
  447.      * future swing releases.  The current serialization support is appropriate
  448.      * for short term storage or RMI between Swing1.0 applications.  It will
  449.      * not be possible to load serialized Swing1.0 objects with future releases
  450.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  451.      * baseline for the serialized form of Swing objects.
  452.      */
  453.     protected class AccessibleJEditorPane extends AccessibleJTextComponent {
  454.  
  455.         /**
  456.          * Gets the accessibleDescription property of this object.  If this
  457.          * property isn't set, return the content type of this JEditorPane
  458.          * instead (e.g. "plain/text", "html/text", etc.
  459.          *
  460.          * @return the localized description of the object; null if
  461.          * this object does not have a description
  462.          *
  463.          * @see #setAccessibleName
  464.          */
  465.         public String getAccessibleDescription() {
  466.             if (accessibleDescription != null) {
  467.                 return accessibleDescription;
  468.             } else {
  469.                 return JEditorPane.this.getContentType();
  470.             }
  471.         }
  472.  
  473.         /**
  474.          * Gets the state set of this object.
  475.          *
  476.          * @return an instance of AccessibleStateSet describing the states
  477.          * of the object
  478.          * @see AccessibleStateSet
  479.          */
  480.         public AccessibleStateSet getAccessibleStateSet() {
  481.             AccessibleStateSet states = super.getAccessibleStateSet();
  482.             states.add(AccessibleState.MULTI_LINE);
  483.             return states;
  484.         }
  485.     }
  486.  
  487.     private static Hashtable getKitTypeRegistry() {
  488.         Hashtable kitTypeRegistry = 
  489.             (Hashtable)SwingUtilities.appContextGet(kitTypeRegistryKey);
  490.         if (kitTypeRegistry == null) {
  491.             kitTypeRegistry = new Hashtable();
  492.             SwingUtilities.appContextPut(kitTypeRegistryKey, kitTypeRegistry);
  493.         }
  494.         return kitTypeRegistry;
  495.     }
  496.  
  497.     // --- variables ---------------------------------------
  498.  
  499.     /**
  500.      * Current content binding of the editor.
  501.      */
  502.     private EditorKit kit;
  503.  
  504.     /**
  505.      * Table of registered type handlers for this editor.
  506.      */
  507.     private Hashtable typeHandlers;
  508.  
  509.     /*
  510.      * Private AppContext keys for this class's static variables.
  511.      */
  512.     private static final Object kitRegistryKey = 
  513.         new StringBuffer("JEditorPane.kitRegistry");
  514.     private static final Object kitTypeRegistryKey = 
  515.         new StringBuffer("JEditorPane.kitTypeRegistry");
  516.  
  517.     static {
  518.         // set the default bindings
  519.         registerEditorKitForContentType("text/plain", "com.sun.java.swing.JEditorPane$PlainEditorKit");
  520.         registerEditorKitForContentType("text/html", "com.sun.java.swing.text.html.HTMLEditorKit");
  521.         registerEditorKitForContentType("text/rtf", "com.sun.java.swing.text.rtf.RTFEditorKit");
  522.         registerEditorKitForContentType("application/rtf", "com.sun.java.swing.text.rtf.RTFEditorKit");
  523.     }
  524.  
  525.     
  526.     static class PlainEditorKit extends DefaultEditorKit implements ViewFactory {
  527.  
  528.     /**
  529.      * Fetches a factory that is suitable for producing 
  530.      * views of any models that are produced by this
  531.      * kit.  The default is to have the UI produce the
  532.      * factory, so this method has no implementation.
  533.      *
  534.      * @return the view factory
  535.      */
  536.         public ViewFactory getViewFactory() {
  537.         return this;
  538.     }
  539.  
  540.     /**
  541.      * Creates a view from the given structural element of a
  542.      * document.
  543.      *
  544.      * @param elem  the piece of the document to build a view of
  545.      * @return the view
  546.      * @see View
  547.      */
  548.         public View create(Element elem) {
  549.         return new WrappedPlainView(elem);
  550.     }
  551.  
  552.     }
  553.  
  554. }
  555.